//=============================================================================
/**
 *
 *	@file		break_ai.c
 *	@brief		かわらわりAI(親のみ呼び出し)
 *	@author		hosaka genya
 *	@data		2008.11.19
 *
 */
//=============================================================================
#include "common.h"
#include "../../pokethlon.h"

#include "break_common.h"
#include "break_poke.h"
#include "break_ai.h"


typedef struct BREAK_AI_tag * BREAK_AI_PTR;

//=============================================================================
/**
 *								定数定義
 */
//=============================================================================
enum
{
	AI_MAX = ENTRY_MEMBER_NUM_MAX-1,	///< AIで管理する最大数
};

enum {
	AI_RES_NULL		= 0,
	AI_RES_ATTACK	= (1),
	AI_RES_CRITICAL	= (1<<1),
} AI_PROC_RESULT_BIT_FLAG;

typedef enum
{
	AI_STATE_NORMAL = 0,
	AI_STATE_CHANGE,
	AI_STATE_PANIC,
	AI_STATE_ADD_WAIT,
} AI_STATE;

//=============================================================================
/**
 *								構造体定義
 */
//=============================================================================

//--------------------------------------------------------------
///
//==============================================================
typedef struct BREAK_AI_tag{
	PKTHLN_PTR work;

	// 4 * 3 = 12
	BREAK_POKE_PARAM Param[ ENTRY_MAX ];

	// 4
	s32 KawaraHP;

	// 4
	u32 BreakCount : 8;		///< 割った枚数
	u32 ActionTimer : 8;
	u32 Tension : 8;
	u32 State : 6;			///< AI_STATE
	u32 PokeIdx : 2;

	// 4
	u32 HealTimer : 11;
	u32 TensionTimer : 11;
	u32 ActionRate : 8;		///< 次に行動するまでの時間
	u32 NetID : 2;

	// 4
	u32 AttackTimer : 8;
	u32 bAttack : 1;
	u32 bHighTension : 1;
	u32 DummyBit : 23;
} BREAK_AI;

//--------------------------------------------------------------
///
//==============================================================
typedef struct BREAK_AI_CNT_tag {
	// in
	HEAPID HeapID;
	PKTHLN_PTR PkthlnWork;
	// private
	BREAK_AI AI[ AI_MAX ];
	u32 AI_Count;
} BREAK_AI_CNT;

//=============================================================================
/**
 *							プロトタイプ宣言
 */
//=============================================================================
static void BreakAI_Setup( BREAK_AI_PTR ptr, u8 NetID, PKTHLN_PTR work );
static u32 BreakAI_Proc( BREAK_AI_PTR ptr );
static u32 AI_Proc_Normal( BREAK_AI_PTR ptr );
static u32 AI_Proc_Change( BREAK_AI_PTR ptr );
static u32 AI_Proc_AddWait( BREAK_AI_PTR ptr );
static u32 AI_Proc_Panic( BREAK_AI_PTR ptr );
static BOOL AI_CheckCanselAttack( BREAK_AI_PTR ptr );
static BOOL AI_CheckCritical( BREAK_AI_PTR ptr );
static BOOL AI_CheckChange( BREAK_AI_PTR ptr );
static void AI_SetState( BREAK_AI_PTR ptr, AI_STATE state );
static void AI_Attack( BREAK_AI_PTR ptr, BOOL bCritical );
static void AI_Comm( BREAK_AI_PTR ptr, u32 Result, PKTHLN_PTR work );
static void AI_Tension_Add( BREAK_AI_PTR ptr, BOOL bCritical, u16 breakNum );
static void AI_Tension_Down( BREAK_AI_PTR ptr );
static void AI_HighTensionEnd( BREAK_AI_PTR ptr );

//=============================================================================
/**
 *								外部公開関数
 */
//=============================================================================


//-----------------------------------------------------------------------------
/**
 *	@brief	AI管理 モジュール生成
 *
 *	@param	PKTHLN_PTR work
 *	@param	HeapID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
BREAK_AI_CNT_PTR BreakAICnt_Create( PKTHLN_PTR work, HEAPID HeapID )
{
	int i;
	BREAK_AI_CNT_PTR cnt;
	u8 PlayerNum;

	PlayerNum = PTFrame_GetPlayerNum(work);

	cnt = sys_AllocMemory( HeapID, sizeof(BREAK_AI_CNT) );
	MI_CpuClear8( cnt, sizeof(BREAK_AI_CNT) );

	cnt->HeapID = HeapID;
	cnt->PkthlnWork = work;
	cnt->AI_Count = ENTRY_MEMBER_NUM_MAX - PlayerNum;

	GF_ASSERT( cnt->AI_Count <= AI_MAX );

	for( i=0; i<cnt->AI_Count; i++ )
	{
		BreakAI_Setup( &cnt->AI[i], PlayerNum + i, work );
	}

//	HOSAKA_PRINT("AI_Count:%d \n", cnt->AI_Count);

	return cnt;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AI管理 モジュール削除
 *
 *	@param	BREAK_AI_CNT_PTR cnt
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void BreakAICnt_Delete( BREAK_AI_CNT_PTR cnt )
{
	sys_FreeMemoryEz( cnt );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AI管理 主処理
 *
 *	@param	BREAK_AI_CNT_PTR cnt
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void BreakAICnt_Proc( BREAK_AI_CNT_PTR cnt )
{
	int i;

	for( i=0; i<cnt->AI_Count; i++ )
	{
		BREAK_AI_PTR ptr;
		u32 Result;

		ptr = &cnt->AI[i];
		Result = BreakAI_Proc( ptr );
		AI_Comm( ptr, Result, cnt->PkthlnWork );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AI通信情報セット（初期化用)
 *
 *	@param	BREAK_AI_CNT_PTR cnt
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void BreakAICnt_Comm( BREAK_AI_CNT_PTR cnt )
{
	int i;

	for( i=0; i<cnt->AI_Count; i++ )
	{
		AI_Comm( &cnt->AI[i], 0, cnt->PkthlnWork );
	}
}

//=============================================================================
/**
 *								static関数
 */
//=============================================================================

//-----------------------------------------------------------------------------
/**
 *	@brief	AI 初期化
 *
 *	@param	BREAK_AI_PTR ptr
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void BreakAI_Setup( BREAK_AI_PTR ptr, u8 NetID, PKTHLN_PTR work )
{
	HOSAKA_PRINT("*** AI Setup [%d] ***\n", NetID );
	MI_CpuClear8( ptr, sizeof(BREAK_AI) );
	ptr->work = work;
	ptr->NetID = NetID;
	ptr->KawaraHP = KAWARA_HP_MAX;
	SetBreakPokeParam( ptr->Param, NetID, work );	///< パラメータ取得
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AI 主処理
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static u32 BreakAI_Proc( BREAK_AI_PTR ptr )
{
	u32 Result = 0;

	GF_ASSERT(ptr);

	switch( ptr->State )
	{
		case AI_STATE_NORMAL :
			Result = AI_Proc_Normal( ptr );
			break;
		case AI_STATE_CHANGE :
			Result = AI_Proc_Change( ptr );
			break;
		case AI_STATE_PANIC :
			Result = AI_Proc_Panic( ptr );
			break;
		case AI_STATE_ADD_WAIT :
			Result = AI_Proc_AddWait( ptr );
			break;
		default : GF_ASSERT(0);
	}

	// かわら補給時以外
	if( ptr->State != AI_STATE_ADD_WAIT )
	{
		// テンション下げる
		AI_Tension_Down( ptr );
	}

	// スタミナ回復
	// ※現状、交代した瞬間に次のポケモンの処理に入ってしまう仕様
	StaminaProc( ptr->Param, ptr->HealTimer, ptr->PokeIdx );
	ptr->HealTimer++;

	return Result;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AI シーケンス ノーマル
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static u32 AI_Proc_Normal( BREAK_AI_PTR ptr )
{
	u32 Result = 0;

	// 行動値チェック
	if( ++ptr->ActionTimer >= ptr->ActionRate )
	{
		// 交代チェック
		if( AI_CheckChange( ptr ) )
		{
			// 交代
			AI_HighTensionEnd( ptr );
			// AP:交代
			PTFrame_AddActionPoint( ptr->work, ptr->NetID, ptr->PokeIdx, ADD_TYPE_CHANGE, 1 );
			AI_SetState( ptr, AI_STATE_CHANGE );
		}
		else
		{
			// クリティカルチェック
			if( AI_CheckCritical( ptr ) )
			{
				Result += AI_RES_CRITICAL;
			}

			// State:攻撃中？
			if(ptr->bAttack )
			{
				// 攻撃キャンセルチェック
				if( ptr->AttackTimer >= ATTACK_DERAY_SYNC && AI_CheckCanselAttack( ptr ) )
				{
					AI_Attack(ptr, (Result & AI_RES_CRITICAL) );
				}
			}
			else
			{
				AI_Attack(ptr, (Result & AI_RES_CRITICAL) );
			}
		}

		// 初期化、再抽選
		ptr->ActionTimer = 0;
		ptr->ActionRate = AI_ATTACK_SYNC_LOW + ( gf_mtRand() % ( AI_ATTACK_SYNC_HIGH-AI_ATTACK_SYNC_LOW ) );
	}

	// 攻撃ウェイト
	if( ptr->bAttack )
	{
		if( ++ptr->AttackTimer >= ATTACK_SYNC )
		{
			ptr->AttackTimer = 0;
			ptr->bAttack = 0;
		}
	}

	return Result;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AI シーケンス 交代
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static u32 AI_Proc_Change( BREAK_AI_PTR ptr )
{
	u32 Result = 0;

	if( ++ptr->ActionTimer >= AI_CHANGE_SYNC )
	{
		AI_SetState( ptr, AI_STATE_NORMAL );
	}

	return Result;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	かわら補充ウェイト
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static u32 AI_Proc_AddWait( BREAK_AI_PTR ptr )
{
	u32 Result = 0;

	if( ++ptr->ActionTimer >= AI_ADD_IN_WAIT_SYNC )
	{
		AI_SetState( ptr, AI_STATE_NORMAL );
	}

	return Result;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	気絶ウェイト
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static u32 AI_Proc_Panic( BREAK_AI_PTR ptr )
{
	u32 Result = 0;
	BREAK_POKE_PARAM* bpp = &ptr->Param[ ptr->PokeIdx ];

	if( bpp->Stamina >= PANIC_REFLESH_POINT )
	{
		AI_SetState( ptr, AI_STATE_NORMAL );
	}

	return Result;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	攻撃動作キャンセルを行うか判定
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval	TRUE:攻撃を行う
 */
//-----------------------------------------------------------------------------
static BOOL AI_CheckCanselAttack( BREAK_AI_PTR ptr )
{
	int i;
	BREAK_POKE_PARAM* bpp;
	int Parsent = 0;
	u8 Rand = gf_mtRand() % 100;

	GF_ASSERT( ptr );

	bpp = &ptr->Param[ ptr->PokeIdx ];

	for( i=0; i<CANSEL_STA_TBL_MAX; i++ )
	{
		if( bpp->Stamina >= g_Cansel_StaminaCheck[i] )
		{
			Parsent += g_Cansel_StaminaAddParsent[i];
			break;
		}
	}

	for( i=0; i<CANSEL_TEN_TBL_MAX; i++ )
	{
		if( ptr->Tension >= g_Cansel_TensionCheck[i] )
		{
			Parsent += g_Cansel_TensionAddParsent[i];
			break;
		}
	}

//	HOSAKA_PRINT("[%d]HitCansel Rand:%d Parsent:%d \n",ptr->NetID, Rand, Parsent );

	return ( Rand < Parsent );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	クリティカルチェック
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL AI_CheckCritical( BREAK_AI_PTR ptr )
{
	int Parsent = 0;
	u8 Rand = gf_mtRand() % 100;
	u8 Tec  = ptr->Param[ ptr->PokeIdx ].Tec;

	GF_ASSERT( Tec < AI_CRITICAL_TBL_MAX );

	Parsent += c_AI_CriticalParTbl[ Tec ];

//	HOSAKA_PRINT("[%d]Critical Rand:%d Parsent:%d \n", ptr->NetID, Rand, Parsent);

	return ( Rand < Parsent );
}

//-----------------------------------------------------------------------------
/**
 *	@brief
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL AI_CheckChange( BREAK_AI_PTR ptr )
{
	int i;
	BREAK_POKE_PARAM* bpp;
	int Parsent = 0;
	u8 Rand = gf_mtRand() % 100;

	GF_ASSERT(ptr);

	bpp = &ptr->Param[ ptr->PokeIdx ];

	for( i=0; i<CHANGE_STA_TBL_MAX; i++ )
	{
		if( bpp->Stamina < g_Change_StaminaCheck[i] )
		{
			Parsent += g_Change_StaminaAddParset[i];
			break;
		}
	}

	for( i=0; i<CHANGE_TEN_TBL_MAX; i++ )
	{
		if( ptr->Tension >= g_Change_TensionCheck[i] )
		{
			Parsent += g_Change_TensionAddParsent[i];
			break;
		}
	}

//	HOSAKA_PRINT("[%d]ChangeCheck Rand:%d Parsent:%d \n",ptr->NetID, Rand, Parsent );

	return ( Rand < Parsent );
}

//-----------------------------------------------------------------------------
/**
 *	@brief
 *
 *	@param	BREAK_AI_PTR ptr
 *	@param	state
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void AI_SetState( BREAK_AI_PTR ptr, AI_STATE state )
{
#ifdef PM_DEBUG
	switch( ptr->State )
	{
		case AI_STATE_PANIC:
			HOSAKA_PRINT("[%d] AI Panic recover!! \n", ptr->NetID, ptr->ActionTimer);
			break;
	}
#endif

	// State 初期化処理
	switch( state )
	{
		case AI_STATE_PANIC:
			HOSAKA_PRINT("[%d] AI Panic... \n", ptr->NetID);
			ptr->HealTimer = 0;
			break;

		case AI_STATE_CHANGE:
			// テンション下げる
			{
				int val = ptr->Tension - TENSION_DEC_CHANGE;
				ptr->Tension = MATH_CLAMP( val, 0, TENSION_MAX );
			}
			// 参照ポケモン変更
			ptr->PokeIdx++;
			if( ptr->PokeIdx >= ENTRY_MAX )
			{
				ptr->PokeIdx = 0;
			}
			break;
	}

	// 共通処理
	ptr->State = state;
	ptr->ActionTimer = 0;
}

//-----------------------------------------------------------------------------
/**
 *	@brief
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void AI_Attack( BREAK_AI_PTR ptr, BOOL bCritical )
{
	BREAK_POKE_PARAM* bpp;
	s16 Damage;
	u16 BreakNum = 0;	///< 今回の破壊枚数

	GF_ASSERT(ptr);

	bpp = &ptr->Param[ ptr->PokeIdx ];

	// カワラ破壊処理
	Damage = bpp->Pow;

	if( bCritical ){ Damage += DAMAGE_CRITICAL; }

	while( 1 )
	{
		ptr->KawaraHP -= Damage;

		// かわら破壊判定
		if( ptr->KawaraHP <= 0 )
		{
			Damage = ptr->KawaraHP * -1;
			ptr->KawaraHP = KAWARA_HP_MAX;
			// AP:獲得
			PTFrame_AddActionPoint( ptr->work, ptr->NetID, ptr->PokeIdx, ADD_TYPE_GET, 1 );
			BreakNum++;
		}
		else
		{
			break;
		}
	}

	// 破壊枚数加算
	ptr->BreakCount += BreakNum;

	if( ptr->bHighTension == FALSE )
	{
		// 攻撃キャンセル
		if( ptr->bAttack )
		{
			// スタミナ減算
			bpp->Stamina = StaminaCalc( bpp->Stamina, ptr->AttackTimer );
			// 気絶判定
			if( bpp->Stamina <= 0 )
			{
				// AP:失敗
				PTFrame_AddActionPoint( ptr->work, ptr->NetID, ptr->PokeIdx, ADD_TYPE_FAIL, 1 );
				AI_SetState( ptr, AI_STATE_PANIC );
				return; // 攻撃動作フラグも立てない
			}
#ifdef PM_DEBUG
//			HOSAKA_PRINT("[%d]AI stamina:%d \n",ptr->NetID, bpp->Stamina);
#endif
		}

		// テンションアップ処理
		AI_Tension_Add( ptr, bCritical, BreakNum );
	}

	// ブロック補給判定
	if( BreakNum > 0 && ptr->BreakCount > 0 && ptr->BreakCount % 10 == 0 )
	{
		// 補給ウェイト
		AI_SetState( ptr, AI_STATE_ADD_WAIT );
	}

	// 攻撃動作フラグを立てる
	ptr->bAttack = 1;
	ptr->AttackTimer = 0;

	// AP:体当たり
	PTFrame_AddActionPoint( ptr->work, ptr->NetID, ptr->PokeIdx, ADD_TYPE_ATTACK, 1 );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AIの算出データを親機のReciveDataにセット
 *
 *	@param	BREAK_AI_PTR ptr
 *	@param	Result
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void AI_Comm( BREAK_AI_PTR ptr, u32 Result, PKTHLN_PTR work )
{
	COMM_EVENT_COMMON_DATA* data = PTFrame_GetEvtComCmnData(work);
	BREAK_CHILD_DATA* child_rec = PKTHLN_CommPack_GetData(&data->ReciveData[ptr->NetID]);
	BREAK_POKE_PARAM* bpp;

	bpp = &ptr->Param[ ptr->PokeIdx ];

	child_rec->BreakCount = ptr->BreakCount;
	child_rec->Stamina = bpp->Stamina;
	child_rec->Tension = ptr->Tension;
	child_rec->bHighTension = ptr->bHighTension;
	child_rec->bCritical = (Result & AI_RES_CRITICAL);
	child_rec->PokeIdx = ptr->PokeIdx;
	child_rec->bChange = (ptr->State == AI_STATE_CHANGE);


#if 0
	if( ptr->bHighTension )
	{
		HOSAKA_PRINT("AI[%d] HighTension! State:%d TensionTimer:%d \n", ptr->NetID, ptr->State, ptr->TensionTimer );
	}
#endif

//	HOSAKA_PRINT("AI send >> [%d]BreakCount=%d \n", ptr->NetID, ptr->BreakCount );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	テンション増加
 *
 *	@param	BREAK_AI_PTR ptr
 *	@param	bCritical
 *	@param	breakNum
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void AI_Tension_Add( BREAK_AI_PTR ptr, BOOL bCritical, u16 breakNum )
{
	GF_ASSERT( ptr->bHighTension == FALSE );

	if( bCritical ){ ptr->Tension += TENSION_ADD_CRITICAL; }
	if( breakNum ){ ptr->Tension += TENSION_ADD_BREAK * breakNum; }
	if( ptr->Tension > TENSION_MAX ){ ptr->Tension = TENSION_MAX; }

	if( ptr->Tension >= TENSION_MAX )
	{
		// ハイテンション移行
		ptr->bHighTension = TRUE;
		ptr->TensionTimer = 0;
	}

//	HOSAKA_PRINT("[%d]AI tenison : %d \n",ptr->NetID, ptr->Tension );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	テンション下げる
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void AI_Tension_Down( BREAK_AI_PTR ptr )
{
	// ハイテンション中
	if( ptr->bHighTension )
	{
		if( ++ptr->TensionTimer >= TENSION_SYNC )
		{
			// ハイテンション終了
			AI_HighTensionEnd( ptr );
		}
	}
	// 通常時
	else
	{
		if( ptr->Tension > 0 )
		{
			if( ++ptr->TensionTimer >= TENSION_DEC_SYNC )
			{
				ptr->TensionTimer = 0;
				ptr->Tension--;
			}
		}
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief
 *
 *	@param	BREAK_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void AI_HighTensionEnd( BREAK_AI_PTR ptr )
{
	BREAK_POKE_PARAM* bpp;
	bpp = &ptr->Param[ ptr->PokeIdx ];

	ptr->bHighTension = 0;
	ptr->TensionTimer = 0;
	ptr->Tension = 0;
	bpp->Stamina = TENSION_STAMINA_RESET;
}
